// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package main

import (
	"go/ast"
	"go/token"
	"reflect"
)

type simplifier struct{}

func (s simplifier) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.CompositeLit:
		// 数组，切片，和map复合文字可能被简化
		outer := n
		var keyType, eltType ast.Expr
		switch typ := outer.Type.(type) {
		case *ast.ArrayType:
			eltType = typ.Elt
		case *ast.MapType:
			keyType = typ.Key
			eltType = typ.Value
		}

		if eltType != nil {
			var ktyp reflect.Value
			if keyType != nil {
				ktyp = reflect.ValueOf(keyType)
			}
			typ := reflect.ValueOf(eltType)
			for i, x := range outer.Elts {
				px := &outer.Elts[i]
				// 查看索引/命名元素的值
				if t, ok := x.(*ast.KeyValueExpr); ok {
					if keyType != nil {
						s.simplifyLiteral(ktyp, keyType, t.Key, &t.Key)
					}
					x = t.Value
					px = &t.Value
				}
				s.simplifyLiteral(typ, eltType, x, px)
			}
			// 节点被简化-stop walk（没有可简化的子节点）
			return nil
		}

	case *ast.SliceExpr:
		// 形式的切片表达式：s[a:len（s）]
		// 可以简化为：s[a:]
		// 如果s“足够简单”（目前我们只接受标识符）
		// 
		// 注意：这可能不正确，因为len可能已在属于同一软件包的另一个
		// 文件中重新声明。然而，这是极不可能的
		// 到目前为止（2016年4月，在多年支持这一重写功能后），
		// 从未出现过，所以让我们继续保持它的工作状态（另见#15153）。
		if n.Max != nil {
			// /-3-索引切片始终需要第二和第三个索引
			break
		}
		if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
			// 数组/切片对象是单个已解析标识符
			if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() {
				// 高表达式是带有单个参数的函数调用
				if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil {
					// 调用的函数是“len”，并且不是本地定义的；
					// 因为我们没有点导入，它必须是预定义的len（）
					if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj {
						// len参数是数组/切片对象
						n.High = nil
					}
				}
			}
		}
		// 注意：我们也可以将形式为s[0:b]的切片表达式简化为s[：b]
		// 但我们保持原样，因为有时我们想要非常明确的
		// 关于下限。
		// 一个0有帮助的例子：
		// x，y，z:=b[0:2]，b[2:4]，b[4:6]
		// 一个它没有帮助的例子：
		// x，y:=b[：n]，b[n:]

	case *ast.RangeStmt:
		// 可以简化为：对于x=range v{…}
		// /-形式的范围：for=范围v{…}
		// 可以简化为：对于范围v{…}
		if isBlank(n.Value) {
			n.Value = nil
		}
		if isBlank(n.Key) && n.Value == nil {
			n.Key = nil
		}
	}

	return s
}

func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) {
	ast.Walk(s, x) // simplify x 

	// 如果元素是复合文字，且其文字类型
	// 与外部文字的元素类型完全匹配，内部
	// 文字类型可以省略
	if inner, ok := x.(*ast.CompositeLit); ok {
		if match(nil, typ, reflect.ValueOf(inner.Type)) {
			inner.Type = nil
		}
	}
	// 如果外部文字的元素类型是指针类型*T 
	// 并且元素是类型T的复合文字的和，则内部
	// 可以省略。
	if ptr, ok := astType.(*ast.StarExpr); ok {
		if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
			if inner, ok := addr.X.(*ast.CompositeLit); ok {
				if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
					inner.Type = nil // drop T 
					*px = inner      // drop&
				}
			}
		}
	}
}

func isBlank(x ast.Expr) bool {
	ident, ok := x.(*ast.Ident)
	return ok && ident.Name == "_"
}

func simplify(f *ast.File) {
	// 删除空声明，如“const（）”等
	removeEmptyDeclGroups(f)

	var s simplifier
	ast.Walk(s, f)
}

func removeEmptyDeclGroups(f *ast.File) {
	i := 0
	for _, d := range f.Decls {
		if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) {
			f.Decls[i] = d
			i++
		}
	}
	f.Decls = f.Decls[:i]
}

func isEmpty(f *ast.File, g *ast.GenDecl) bool {
	if g.Doc != nil || g.Specs != nil {
		return false
	}

	for _, c := range f.Comments {
		// 如果声明中有注释，则不视为空
		if g.Pos() <= c.Pos() && c.End() <= g.End() {
			return false
		}
	}

	return true
}
